home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / net / bind-contrib.tar.gz / bind-contrib.tar / contrib / host / malloc.c < prev    next >
C/C++ Source or Header  |  1996-12-02  |  18KB  |  670 lines

  1. /*
  2.  * malloc/free/realloc memory management routines.
  3.  *
  4.  * This is a very simple, but fast storage allocator. It allocates blocks
  5.  * of a small number of different sizes, and keeps free lists of each size.
  6.  * Blocks that don't exactly fit are passed up to the next larger size.
  7.  * In this implementation, the available sizes are (2^(i+4))-8 bytes long.
  8.  * This is designed for use in a single-threaded virtual memory environment.
  9.  *
  10.  * This version is derived from: malloc.c (Caltech) 2/21/82, Chris Kingsley,
  11.  * kingsley@cit-20, and a similar version of Larry Wall, used by perl.
  12.  *
  13.  * Rewritten by Eric Wassenaar, Nikhef-H, <e07@nikhef.nl>
  14.  *
  15.  * Not only varies the vendor-supplied implementation of this package greatly
  16.  * for different platforms, there are also many subtle semantic differences,
  17.  * especially for anomalous conditions. This makes it risky to use a separate
  18.  * package, in case other library routines depend on those special features.
  19.  * Nevertheless, we assume that external routines under normal circumstances
  20.  * just need the basic malloc/free/realloc functionality.
  21.  *
  22.  * To avoid possible conflicts, included in this package are some auxiliary
  23.  * functions: memalign, calloc/cfree, valloc/vfree, because they are closely
  24.  * related to and built upon the basic functions.
  25.  *
  26.  * Not supported are special functions: mallopt/mallinfo/mstats, and those
  27.  * which are found on specific platforms only: mallocblksize/recalloc (sgi),
  28.  * malloc_debug/malloc_verify (sun), malloc_size/malloc_error (next).
  29.  *
  30.  * The simplicity of this package imposes the following limitations:
  31.  * - Memory once allocated is never returned to the system, but is put on
  32.  *   free lists for subsequent use. The process size grows monotonously.
  33.  * - Allocating zero-size data blocks is not an error.
  34.  * - In case realloc fails, the old block may no longer be allocated.
  35.  * - The strictest alignment for memalign is the page size.
  36.  */
  37.  
  38. #ifndef lint
  39. static char Version[] = "@(#)malloc.c    e07@nikhef.nl (Eric Wassenaar) 961019";
  40. #endif
  41.  
  42. #if defined(apollo) && defined(lint)
  43. #define __attribute(x)
  44. #endif
  45.  
  46. #include <stdio.h>
  47. #include <errno.h>
  48. #include <sys/types.h>
  49. #include <sys/param.h>
  50.  
  51. #ifdef lint
  52. #define EXTERN
  53. #else
  54. #define EXTERN extern
  55. #endif
  56.  
  57. EXTERN int errno;
  58.  
  59. /*
  60.  * Portability definitions. These are probably too primitive, but the
  61.  * semantics on various platforms are too chaotic to do it correctly.
  62.  */
  63.  
  64. #if defined(SYSV) || defined(SVR4)
  65. #define SYSV_MALLOC
  66. #define SYSV_MEMSET
  67. #endif
  68.  
  69. #ifdef SYSV_MALLOC
  70. typedef void    ptr_t;
  71. typedef u_int    siz_t;
  72. typedef void    free_t;
  73. #define free_return(x)    return
  74. #else
  75. typedef char    ptr_t;
  76. typedef u_int    siz_t;
  77. typedef int    free_t;
  78. #define free_return(x)    return(x)
  79. #endif
  80.  
  81. #ifdef SYSV_MEMSET
  82. #define bzero(a,n)    (void) memset(a,'\0',n)
  83. #define bcopy(a,b,n)    (void) memcpy(b,a,n)
  84. #endif
  85.  
  86. /*
  87.  * The page size used to request blocks of system memory.
  88.  */
  89.  
  90. #ifndef PAGESIZE
  91. #ifdef NBPG
  92.  
  93. #ifdef CLSIZE
  94. #define PAGESIZE (NBPG*CLSIZE)
  95. #else
  96. #define PAGESIZE NBPG
  97. #endif
  98.  
  99. #else /*not NBPG*/
  100.  
  101. #ifdef NBPC
  102. #define PAGESIZE NBPC
  103. #else
  104. #define PAGESIZE 4096        /* some reasonable default */
  105. #endif
  106.  
  107. #endif /*NBPG*/
  108. #endif
  109.  
  110. static int pagesize = 0;    /* page size after initialization */
  111.  
  112. /*
  113.  * The overhead on a block is 8 bytes on traditional 32-bit platforms.
  114.  * When free, this space contains a pointer to the next free block,
  115.  * and the lower 3 bits of this pointer must be zero.
  116.  * When in use, the first field is set to OVMAGIC with the lower 3 bits
  117.  * nonzero, and the second field is the hash bucket index.
  118.  * The overhead information precedes the data area returned to the user.
  119.  * When special alignment is required, a fragment is created within the
  120.  * data block, preceded by a special fragment header containing a magic
  121.  * number FRMAGIC and the align offset from the overlapping block.
  122.  */
  123.  
  124. typedef union overhead {
  125.  
  126.     struct {
  127.         union overhead *ovfree_next;    /* next chunk on free list */
  128.     } ov_free;
  129.  
  130.     struct {
  131.         int ovused_magic;        /* magic number */
  132.         int ovused_index;        /* bucket index */
  133.     } ov_used;
  134.  
  135.     struct {
  136.         int ovfrag_fmagic;        /* fragment magic number */
  137.         int ovfrag_offset;        /* fragment align offset */
  138.     } ov_frag;
  139.  
  140.     /* align on double word boundary */
  141.     double    ov_align;
  142.  
  143. } OVHDR;
  144.  
  145. #define    ov_next        ov_free.ovfree_next    /* next on free list */
  146. #define    ov_magic    ov_used.ovused_magic    /* magic number */
  147. #define    ov_index    ov_used.ovused_index    /* bucket index */
  148. #define    ov_fmagic    ov_frag.ovfrag_fmagic    /* fragment magic number */
  149. #define    ov_offset    ov_frag.ovfrag_offset    /* fragment align offset */
  150.  
  151. #define    OVMAGIC        0xef            /* overhead magic number */
  152. #define    FRMAGIC        0x5555            /* fragment magic number */
  153. #define OVHDRSZ        sizeof(OVHDR)        /* size of overhead data */
  154.  
  155. /*
  156.  * freelist[i] is the pointer to the next free chunk of size 2^(i+4).
  157.  * The smallest allocatable chunk is 16 bytes, containing a user data
  158.  * block of 8 bytes (assuming 8 bytes overhead on 32-bit platforms).
  159.  */
  160.  
  161. #define MINCHUNK    4    /* minimum chunk size 2^(0+4)  */
  162. #define MAXCHUNK    30    /* maximum chunk size 2^(26+4) */
  163. #define    NBUCKETS    27    /* MAXCHUNK - MINCHUNK + 1 */
  164.  
  165. static OVHDR *freelist[NBUCKETS];    /* hash list of free chunks */
  166. static int malloced[NBUCKETS];        /* number of allocated chunks */
  167.  
  168. #define chunk_size(i)    (1 << ((i) + MINCHUNK))
  169. #define data_size(i)    (chunk_size(i) - OVHDRSZ)
  170. #define MINDATA        ((1 << MINCHUNK) - OVHDRSZ)
  171. #define MAXDATA        ((1 << MAXCHUNK) - OVHDRSZ)
  172.  
  173. #define get_offset(a,b)    ((unsigned long)(a) & ((b) - 1))
  174. #define word_offset(a)    get_offset(a, OVHDRSZ)
  175. #define page_offset(a)    get_offset(a, PAGESIZE)
  176. #define aligned(a,b)    (get_offset(a,b) == 0)
  177. #define word_aligned(a)    aligned(a, OVHDRSZ)
  178. #define page_aligned(a)    aligned(a, PAGESIZE)
  179. #define auto_aligned(a)    aligned(a, a)
  180.  
  181. #define valid_index(i)    ((i) >= 0 && (i) < NBUCKETS)
  182. #define valid_offset(i)    ((i) > OVHDRSZ && auto_aligned(i) && (i) <= PAGESIZE)
  183.  
  184. /*
  185.  * Definition of modules.
  186.  */
  187.  
  188. #define PROTO(TYPES)    ()
  189.  
  190. ptr_t *malloc        PROTO((siz_t));
  191. ptr_t *memalign        PROTO((siz_t, siz_t));
  192. free_t free        PROTO((ptr_t *));
  193. ptr_t *realloc        PROTO((ptr_t *, siz_t));
  194. ptr_t *calloc        PROTO((siz_t, siz_t));
  195. free_t cfree        PROTO((ptr_t *));
  196. ptr_t *valloc        PROTO((siz_t));
  197. free_t vfree        PROTO((ptr_t *));
  198.  
  199. extern ptr_t *sbrk    PROTO((int));
  200.  
  201. /*
  202. ** MALLOC -- Allocate more memory
  203. ** ------------------------------
  204. */
  205.  
  206. ptr_t *
  207. malloc(size)
  208. siz_t size;                /* amount of memory to allocate */
  209. {
  210.     register OVHDR *op;        /* chunk pointer */
  211.     register int bucket;        /* hash bucket index */
  212.     register int bucketsize;    /* size of hash bucket chunk */
  213.     register int memsize;        /* amount of memory to expand */
  214.  
  215. /*
  216.  * First time malloc is called, do some sanity checks, setup page size,
  217.  * and align the break pointer so all chunk data will be page aligned.
  218.  * Note. Cannot issue debugging print statements during initialization.
  219.  */
  220.     if (pagesize == 0)
  221.     {
  222.         if (page_offset(PAGESIZE) || word_offset(OVHDRSZ))
  223.         {
  224.             errno = EINVAL;
  225.             return(NULL);
  226.         }
  227.  
  228.         op = (OVHDR *)sbrk(0);
  229.         if (op == NULL || (char *)op == (char *)-1)
  230.         {
  231.             errno = ENOMEM;
  232.             return(NULL);
  233.         }
  234.  
  235.         memsize = page_offset(op);
  236.         if (memsize > 0)
  237.         {
  238.             memsize = PAGESIZE - memsize;
  239.             op = (OVHDR *)sbrk(memsize);
  240.             if (op == NULL || (char *)op == (char *)-1)
  241.             {
  242.                 errno = ENOMEM;
  243.                 return(NULL);
  244.             }
  245.         }
  246.  
  247.         /* initialization complete */
  248.         pagesize = PAGESIZE;
  249.     }
  250.  
  251. /*
  252.  * Convert amount of memory requested into closest chunk size
  253.  * stored in hash buckets which satisfies request.
  254.  */
  255.     bucket = 0; bucketsize = chunk_size(bucket);
  256.     while (bucketsize < OVHDRSZ || size > (bucketsize - OVHDRSZ))
  257.     {
  258.         bucket++; bucketsize <<= 1;
  259.         if (bucket >= NBUCKETS)
  260.         {
  261.             errno = EINVAL;
  262.             return(NULL);
  263.         }
  264.     }
  265.  
  266. /*
  267.  * If nothing in hash bucket right now, request more memory from the system.
  268.  * Add new memory allocated to that on free list for this hash bucket.
  269.  * System memory is expanded by increments of whole pages. For small chunk
  270.  * sizes, the page is subdivided into a list of free chunks.
  271.  */
  272.     if (freelist[bucket] == NULL)
  273.     {
  274.         memsize = (bucketsize < PAGESIZE) ? PAGESIZE : bucketsize;
  275.         op = (OVHDR *)sbrk(memsize);
  276.         if (op == NULL || (char *)op == (char *)-1)
  277.         {
  278.             errno = ENOMEM;
  279.             return(NULL);
  280.         }
  281.  
  282.         freelist[bucket] = op;
  283.         while (memsize > bucketsize)
  284.         {
  285.             memsize -= bucketsize;
  286.             op->ov_next = (OVHDR *)((char *)op + bucketsize);
  287.             op = op->ov_next;
  288.         }
  289.         op->ov_next = NULL;
  290.     }
  291.  
  292. /*
  293.  * Memory is available.
  294.  */
  295.     /* remove from linked list */
  296.     op = freelist[bucket];
  297.     freelist[bucket] = op->ov_next;
  298.     malloced[bucket]++;
  299.  
  300.     /* mark this chunk in use */
  301.     op->ov_magic = OVMAGIC;
  302.     op->ov_index = bucket;
  303.  
  304.     /* return pointer to user data block */
  305.     return((ptr_t *)((char *)op + OVHDRSZ));
  306. }
  307.  
  308. /*
  309. ** MEMALIGN -- Allocate memory with alignment constraints
  310. ** ------------------------------------------------------
  311. */
  312.  
  313. ptr_t *
  314. memalign(align, size)
  315. siz_t align;                /* required memory alignment */
  316. siz_t size;                /* amount of memory to allocate */
  317. {   
  318.     register OVHDR *op;        /* chunk pointer */
  319.     register ptr_t *newbuf;        /* new block of user data */
  320.     register int offset = 0;    /* fragment offset for alignment */
  321.  
  322. /*
  323.  * The alignment must be a power of two, and no bigger than the page size.
  324.  */
  325.     if ((align == 0) || !auto_aligned(align) || (align > PAGESIZE))
  326.     {
  327.         errno = EINVAL;
  328.         return(NULL);
  329.     }
  330.  
  331. /*
  332.  * For ordinary small alignment sizes, we can use the plain malloc.
  333.  */
  334.     if (align > OVHDRSZ)
  335.         offset = align - OVHDRSZ;
  336.  
  337.     newbuf = malloc(size + offset);
  338.     if (newbuf == NULL)
  339.         return(NULL);
  340.  
  341. /*
  342.  * Otherwise we have to create a more strictly aligned fragment.
  343.  */
  344.     if (offset > 0)
  345.     {
  346.         /* locate the proper alignment boundary within the block */
  347.         newbuf = (ptr_t *)((char *)newbuf + offset);
  348.  
  349.         /* mark this block as a special fragment */
  350.         op = (OVHDR *)((char *)newbuf - OVHDRSZ);
  351.         op->ov_fmagic = FRMAGIC;
  352.         op->ov_offset = OVHDRSZ + offset;
  353.     }
  354.  
  355.     return(newbuf);
  356. }
  357.  
  358. /*
  359. ** DEALLOC -- Put unneeded memory on the free list
  360. ** -----------------------------------------------
  361. */
  362.  
  363. static int
  364. dealloc(oldbuf)
  365. ptr_t *oldbuf;                /* old block of user data */
  366. {   
  367.     register OVHDR *op;        /* chunk pointer */
  368.     register int bucket;        /* hash bucket index */
  369.     register int offset;        /* fragment offset for alignment */
  370.  
  371.     /* if no old block, or if not yet initialized */
  372.     if (oldbuf == NULL || pagesize == 0)
  373.     {
  374.         errno = EINVAL;
  375.         return(0);
  376.     }
  377.  
  378.     /* avoid bogus block addresses */
  379.     if (!word_aligned(oldbuf))
  380.     {
  381.         errno = EINVAL;
  382.         return(0);
  383.     }
  384.  
  385.     /* move to the header for this chunk */
  386.     op = (OVHDR *)((char *)oldbuf - OVHDRSZ);
  387.  
  388.     /* adjust in case this is an aligned fragment */
  389.     if ((op->ov_fmagic == FRMAGIC) && valid_offset(op->ov_offset))
  390.     {
  391.         offset = op->ov_offset - OVHDRSZ;
  392.         op = (OVHDR *)((char *)op - offset);
  393.     }
  394.  
  395.     /* check whether this chunk was really allocated */
  396.     if ((op->ov_magic != OVMAGIC) || !valid_index(op->ov_index))
  397.     {
  398.         errno = EINVAL;
  399.         return(0);
  400.     }
  401.  
  402.     /* put back on the free list for this bucket */
  403.     bucket = op->ov_index;
  404.     op->ov_next = freelist[bucket];
  405.     freelist[bucket] = op;
  406.     malloced[bucket]--;
  407.  
  408.     return(1);
  409. }
  410.  
  411. /*
  412. ** FREE -- Put unneeded memory on the free list
  413. ** --------------------------------------------
  414. **
  415. **    In this implementation, unneeded memory is never returned to
  416. **    the system, but is put on a free memory hash list instead.
  417. **    Subsequent malloc requests will first examine the free lists
  418. **    to see whether a request can be satisfied.
  419. **    As a consequence of this strategy, the process size will grow
  420. **    monotonously, up to the largest amount needed at any moment.
  421. **
  422. **    On some platforms, this routine does not return a status code.
  423. **    The status will be stored in a global variable ``free_status''
  424. **    which can be examined if desired.
  425. */
  426.  
  427. int free_status = 0;            /* return code of last free */
  428.  
  429. free_t
  430. free(oldbuf)
  431. ptr_t *oldbuf;                /* old block of user data */
  432. {   
  433.     free_status = dealloc(oldbuf);
  434.     free_return(free_status);
  435. }
  436.  
  437. /*
  438. ** FINDBUCKET -- Locate a chunk of memory on the free list
  439. ** -------------------------------------------------------
  440. **
  441. **    When a program attempts "storage compaction" as mentioned in the
  442. **    old malloc man page, it realloc's an already freed block. Usually
  443. **    this is the last block it freed; occasionally it might be farther
  444. **    back. We have to search all the free lists for the block in order
  445. **    to determine its bucket: first we make one pass thru the lists
  446. **    checking only the first block in each; if that fails we search
  447. **    the entire lists for a match. If still not found it is an error.
  448. */
  449.  
  450. static int
  451. findbucket(oldop)
  452. OVHDR *oldop;                /* old chunk to search for */
  453. {
  454.     register OVHDR *op;        /* chunk pointer */
  455.     register int bucket;        /* hash bucket index */
  456.  
  457.     for (bucket = 0; bucket < NBUCKETS; bucket++)
  458.     {
  459.         if (freelist[bucket] == oldop)
  460.             return(bucket);
  461.     }
  462.  
  463.     for (bucket = 0; bucket < NBUCKETS; bucket++)
  464.     {
  465.         for (op = freelist[bucket]; op != NULL; op = op->ov_next)
  466.         {
  467.             if (op == oldop)
  468.                 return(bucket);
  469.         }
  470.     }
  471.  
  472.     /* not found */
  473.     return(-1);
  474. }
  475.  
  476. /*
  477. ** REALLOC -- Rellocate already allocated memory
  478. ** ---------------------------------------------
  479. */
  480.  
  481. ptr_t *
  482. realloc(oldbuf, size)
  483. ptr_t *oldbuf;                /* old block of user data */
  484. siz_t size;                /* amount of memory to allocate */
  485. {   
  486.     register OVHDR *op;        /* chunk pointer */
  487.     register ptr_t *newbuf;        /* new block of user data */
  488.     register int bucket;        /* hash bucket index */
  489.     register int offset = 0;    /* fragment offset for alignment */
  490.     siz_t minsize, maxsize;        /* size limits for this bucket */
  491.     int allocated = 0;        /* set if old chunk was allocated */
  492.  
  493. /*
  494.  * Do plain malloc if no old block, or if not yet initialized.
  495.  * Otherwise, get the header, and check for special conditions.
  496.  */
  497.     if (oldbuf == NULL || pagesize == 0)
  498.     {
  499.         newbuf = malloc(size);
  500.         return(newbuf);
  501.     }
  502.  
  503.     /* avoid bogus block addresses */
  504.     if (!word_aligned(oldbuf))
  505.     {
  506.         errno = EINVAL;
  507.         return(NULL);
  508.     }
  509.  
  510.     /* move to the header for this chunk */
  511.     op = (OVHDR *)((char *)oldbuf - OVHDRSZ);
  512.  
  513.     /* adjust in case this is an aligned fragment */
  514.     if ((op->ov_fmagic == FRMAGIC) && valid_offset(op->ov_offset))
  515.     {
  516.         offset = op->ov_offset - OVHDRSZ;
  517.         op = (OVHDR *)((char *)op - offset);
  518.     }
  519.  
  520. /*
  521.  * Check whether this chunk is allocated at this moment.
  522.  * If not, try to locate it on the free list hash buckets.
  523.  */
  524.     if ((op->ov_magic == OVMAGIC) && valid_index(op->ov_index))
  525.     {
  526.         allocated = 1;
  527.         bucket = op->ov_index;
  528.     }
  529.     else
  530.     {
  531.         bucket = findbucket(op);
  532.         if (bucket < 0)
  533.         {
  534.             errno = EINVAL;
  535.             return(NULL);
  536.         }
  537.     }
  538.  
  539. /*
  540.  * If the new size block fits into the same already allocated chunk,
  541.  * we can just use it again, avoiding a malloc and a bcopy. Otherwise,
  542.  * we can safely put it on the free list (the contents are preserved).
  543.  */
  544.     /* make sure the alignment offset is consistent with the bucket */
  545.     if ((offset > 0) && (bucket == 0 || offset > data_size(bucket-1)))
  546.     {
  547.         errno = EINVAL;
  548.         return(NULL);
  549.     }
  550.  
  551.     /* maximum data block size of this chunk */
  552.     maxsize = data_size(bucket) - offset;
  553.  
  554.     if (allocated)
  555.     {
  556.         /* maximum data block size in the preceding hash bucket */
  557.         minsize = (bucket > 0) ? data_size(bucket-1) - offset : 0;
  558.  
  559.         /* re-use same block if within bounds */
  560.         if (size <= maxsize && size > minsize)
  561.             return(oldbuf);
  562.  
  563.         /* no longer useable */
  564.         (void) dealloc(oldbuf);
  565.     }
  566.  
  567. /*
  568.  * A new chunk must be allocated, possibly with alignment restrictions.
  569.  */
  570.     newbuf = malloc(size + offset);
  571.     if (newbuf == NULL)
  572.         return(NULL);
  573.  
  574.     if (offset > 0)
  575.     {
  576.         /* locate the proper alignment boundary within the block */
  577.         newbuf = (ptr_t *)((char *)newbuf + offset);
  578.  
  579.         /* mark this block as a special fragment */
  580.         op = (OVHDR *)((char *)newbuf - OVHDRSZ);
  581.         op->ov_fmagic = FRMAGIC;
  582.         op->ov_offset = OVHDRSZ + offset;
  583.     }
  584.  
  585. /*
  586.  * Copy the contents of the old user data block into the new block.
  587.  * In case we shrink, only copy the requested amount of user data.
  588.  * If we expand, copy the maximum possible amount from the old block.
  589.  * Note that the exact amount of valid old user data is not known.
  590.  */
  591.     if (oldbuf != newbuf)
  592.         bcopy(oldbuf, newbuf, (size < maxsize) ? size : maxsize);
  593.  
  594.     return(newbuf);
  595. }
  596.  
  597. /*
  598. ** CALLOC -- Allocate memory for number of elements, and clear it
  599. ** --------------------------------------------------------------
  600. **
  601. **    This is a wrapper for malloc to request memory for a number
  602. **    of consecutive elements of certain length.
  603. **    As a side effect, the entire memory block obtained is cleared.
  604. */
  605.  
  606. ptr_t *
  607. calloc(count, length)
  608. siz_t count;                /* number of elements */
  609. siz_t length;                /* size per element */
  610. {
  611.     register ptr_t *newbuf;        /* new block of user data */
  612.     register siz_t size;        /* amount of memory to allocate */
  613.  
  614.     size = count * length;
  615.     newbuf = malloc(size);
  616.     if (newbuf == NULL)
  617.         return(NULL);
  618.  
  619.     bzero(newbuf, size);
  620.     return(newbuf);
  621. }
  622.  
  623.  
  624. /*
  625. ** CFREE -- Put unneeded memory on the free list
  626. ** ---------------------------------------------
  627. */
  628.  
  629. free_t
  630. cfree(oldbuf)
  631. ptr_t *oldbuf;                /* old block of user data */
  632. {   
  633.     free_status = dealloc(oldbuf);
  634.     free_return(free_status);
  635. }
  636.  
  637. /*
  638. ** VALLOC -- Allocate memory on a page boundary
  639. ** --------------------------------------------
  640. **
  641. **    This is a wrapper for memalign to request memory that is
  642. **    aligned on a page boundary. In this implementation, this
  643. **    is the strictest alignment possible.
  644. */
  645.  
  646. ptr_t *
  647. valloc(size)
  648. siz_t size;                /* amount of memory to allocate */
  649. {   
  650.     register ptr_t *newbuf;        /* new block of user data */
  651.     siz_t align = PAGESIZE;        /* alignment on page boundary */
  652.  
  653.     newbuf = memalign(align, size);
  654.     return(newbuf);
  655. }
  656.  
  657.  
  658. /*
  659. ** VFREE -- Put unneeded memory on the free list
  660. ** ---------------------------------------------
  661. */
  662.  
  663. free_t
  664. vfree(oldbuf)
  665. ptr_t *oldbuf;                /* old block of user data */
  666. {   
  667.     free_status = dealloc(oldbuf);
  668.     free_return(free_status);
  669. }
  670.